package cn.iocoder.yudao.module.member.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.module.member.utils.dto.AuthOCRCardInfo;
import cn.zhxu.okhttps.HttpResult;
import cn.zhxu.okhttps.OkHttps;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;

/**
 * 身份验证工具类
 * 包含身份证识别、银行卡识别、人脸实名认证等功能
 */
@Slf4j
@Component
public class AuthIdentityUtil {

    /**
     * API认证相关配置
     */
    @Value("${auth.identity.appcode:0001913c1c244f4f9513c5e4ad720530}")
    private String appCode;

    @Value("${auth.identity.secretId:AKID68JWHT4Cx4l92k3dtqnsMVTWV3Rmj0v9uoXC}")
    private String secretId;

    @Value("${auth.identity.secretKey:2i9lG2o7119fskEq40uoK6886a1rjtkv9svF88qc}")
    private String secretKey;

    @Value("${auth.identity.source:market}")
    private String source;

    /**
     * API端点配置
     */
    @Value("${auth.identity.ocr.idcard.endpoint:https://swcardpack.market.alicloudapi.com/ocr/idcard}")
    private String idCardOcrEndpoint;

    @Value("${auth.identity.ocr.bankcard.endpoint:https://swcardpack.market.alicloudapi.com/ocr/bankcard}")
    private String bankCardOcrEndpoint;

    @Value("${auth.identity.face.endpoint:https://zfa.market.alicloudapi.com/efficient/idfaceIdentity}")
    private String faceIdentityEndpoint;

    @Value("${auth.identity.bank.check.endpoint:https://service-e2ikdpmr-1253495967.ap-beijing.apigateway.myqcloud.com/release/v2/bcheck}")
    private String bankCheckEndpoint;

    /**
     * 超时设置(秒)
     */
    @Value("${auth.identity.timeout:10}")
    private int timeout;

    /**
     * 身份证OCR识别
     *
     * @param imageUrl 身份证图片URL
     * @return 身份证信息
     */
    @Cacheable(cacheNames = "member_pic_ocr", key="#imageUrl")
    public AuthOCRCardInfo ocrIdentityCard(String imageUrl) {
        if (StrUtil.isBlank(imageUrl)) {
            log.error("身份证识别失败：图片URL为空");
            throw exception(AUTH_IDENTITY_ID_ERROR);
        }

        log.info("开始进行身份证OCR识别，图片URL: {}", maskSensitiveUrl(imageUrl));
        
        Map<String, String> headers = createAuthHeaders();
        Map<String, String> params = new HashMap<>();
        params.put("image_url", imageUrl);
        
        try {
            HttpResult.Body body = executeHttpRequest(idCardOcrEndpoint, headers, params);
            JSONObject result = parseResultJson(body, "身份证OCR识别");
            
            JSONObject data = result.getJSONObject("result");
            if (data == null) {
                log.error("身份证OCR识别失败：返回数据格式错误");
                throw exception(AUTH_IDENTITY_ID_ERROR);
            }
            
            AuthOCRCardInfo info = new AuthOCRCardInfo();
            info.setName(data.getString("name"));
            info.setId(data.getString("number"));
            info.setAddress(data.getString("address"));
            
            log.info("身份证OCR识别成功，身份证号码后四位: {}", maskSensitiveInfo(info.getId()));
            return info;
        } catch (Exception e) {
            log.error("身份证OCR识别异常", e);
            throw exception(AUTH_IDENTITY_ID_ERROR);
        }
    }

    /**
     * 银行卡OCR识别
     *
     * @param imageUrl 银行卡图片URL
     * @return 银行卡信息
     */
    @Cacheable(cacheNames = "member_pic_ocr", key="#imageUrl")
    public AuthOCRCardInfo ocrBankCard(String imageUrl) {
        if (StrUtil.isBlank(imageUrl)) {
            log.error("银行卡识别失败：图片URL为空");
            throw exception(AUTH_BANK_ID_ERROR);
        }
        
        log.info("开始进行银行卡OCR识别，图片URL: {}", maskSensitiveUrl(imageUrl));
        
        Map<String, String> headers = createAuthHeaders();
        Map<String, String> params = new HashMap<>();
        params.put("image_url", imageUrl);
        
        try {
            HttpResult.Body body = executeHttpRequest(bankCardOcrEndpoint, headers, params);
            JSONObject result = parseResultJson(body, "银行卡OCR识别");
            
            JSONObject data = result.getJSONObject("result");
            if (data == null) {
                log.error("银行卡OCR识别失败：返回数据格式错误");
                throw exception(AUTH_BANK_ID_ERROR);
            }
            
            AuthOCRCardInfo info = new AuthOCRCardInfo();
            info.setName(data.getString("bank_name"));
            info.setId(data.getString("bankcard_number"));
            
            log.info("银行卡OCR识别成功，银行卡号后四位: {}", maskSensitiveInfo(info.getId()));
            return info;
        } catch (Exception e) {
            log.error("银行卡OCR识别异常", e);
            throw exception(AUTH_BANK_ID_ERROR);
        }
    }

    /**
     * 人脸实名认证
     *
     * @param name 姓名
     * @param identityId 身份证号
     * @param image 人脸图片(Base64编码)
     * @return 认证结果，true表示认证成功
     */
    public Boolean checkRealPic(String name, String identityId, String image) {
        if (StrUtil.isBlank(name) || StrUtil.isBlank(identityId) || StrUtil.isBlank(image)) {
            log.error("人脸实名认证失败：参数不完整");
            throw exception(AUTH_IDENTITY_ERROR);
        }
        
        log.info("开始进行人脸实名认证，姓名: {}，身份证号后四位: {}", 
                name, maskSensitiveInfo(identityId));
        
        Map<String, String> headers = createAuthHeaders();
        Map<String, String> params = new HashMap<>();
        params.put("number", identityId);
        params.put("base64Str", image);
        params.put("name", name);
        params.put("liveChk", "0");
        params.put("Threshold", "0.33");
        
        try {
            HttpResult.Body body = executeHttpRequest(faceIdentityEndpoint, headers, params);
            JSONObject result = parseResultJson(body, "人脸实名认证");
            
            JSONObject data = result.getJSONObject("result");
            if (data == null) {
                log.error("人脸实名认证失败：返回数据格式错误");
                throw exception(AUTH_IDENTITY_ERROR);
            }
            
            int validateResult = data.getIntValue("Validate_Result");
            int similarity = data.getIntValue("Similarity");
            
            log.info("人脸实名认证结果: {}, 相似度: {}", validateResult, similarity);
            
            if (validateResult == 1 || (validateResult == 2 && similarity > 600)) {
                return true;
            } else {
                handleFaceValidateError(validateResult);
                return false;
            }
        } catch (Exception e) {
            log.error("人脸实名认证异常", e);
            throw exception(AUTH_IDENTITY_ERROR);
        }
    }

    /**
     * 银行卡信息校验
     *
     * @param name 姓名
     * @param identityId 身份证号
     * @param mobile 手机号
     * @param bankCardId 银行卡号
     * @return 校验结果，true表示校验通过
     */
    public Boolean checkBankCardInfo(String name, String identityId, String mobile, String bankCardId) {
        if (StrUtil.isBlank(name) || StrUtil.isBlank(identityId) || 
            StrUtil.isBlank(mobile) || StrUtil.isBlank(bankCardId)) {
            log.error("银行卡信息校验失败：参数不完整");
            throw exception(AUTH_BANK_ID_ERROR);
        }
        
        log.info("开始进行银行卡信息校验，姓名: {}，身份证号后四位: {}，银行卡号后四位: {}", 
                name, maskSensitiveInfo(identityId), maskSensitiveInfo(bankCardId));
        
        // 生成鉴权信息
        Calendar cd = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        String datetime = sdf.format(cd.getTime());
        String auth = calcAuthorization(source, secretId, secretKey, datetime);

        Map<String, String> headers = new HashMap<>();
        headers.put("X-Source", source);
        headers.put("X-Date", datetime);
        headers.put("Authorization", auth);
        
        Map<String, String> params = new HashMap<>();
        params.put("accountNo", bankCardId);
        params.put("name", name);
        params.put("idCardCode", identityId);
        params.put("bankPreMobile", mobile);
        
        try {
            HttpResult.Body body = executeHttpRequest(bankCheckEndpoint, headers, params);
            JSONObject bodyJson = JSON.parseObject(body.toString());
            
            int errorCode = bodyJson.getIntValue("error_code");
            log.info("银行卡信息校验响应码: {}", errorCode);
            
            if (errorCode == 2831) {
                log.error("银行卡信息校验失败：发卡行不支持此交易");
                throw exception(new ErrorCode(100888, "发卡行不支持此交易，请联系发卡行"));
            } else if (errorCode == 2832) {
                log.error("银行卡信息校验失败：发卡行超时");
                throw exception(new ErrorCode(100888, "发卡行超时"));
            } else if (errorCode == 0) {
                JSONObject data = bodyJson.getJSONObject("result");
                if (data == null) {
                    log.error("银行卡信息校验失败：返回数据格式错误");
                    throw exception(AUTH_BANK_ID_ERROR);
                }
                
                String respCode = data.getString("respCode");
                log.info("银行卡信息校验结果码: {}", respCode);
                
                return "F".equals(respCode);
                } else {
                log.error("银行卡信息校验失败：未知错误码 {}", errorCode);
                throw exception(AUTH_BANK_ID_ERROR);
            }
        } catch (Exception e) {
            log.error("银行卡信息校验异常", e);
            throw exception(AUTH_BANK_ID_ERROR);
        }
    }

    /**
     * 计算鉴权签名
     */
    private String calcAuthorization(String source, String secretId, String secretKey, String datetime) {
        try {
            String signStr = "x-date: " + datetime + "\n" + "x-source: " + source;
            Mac mac = Mac.getInstance("HmacSHA1");
            Key sKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), mac.getAlgorithm());
            mac.init(sKey);
            byte[] hash = mac.doFinal(signStr.getBytes("UTF-8"));
            String sig = Base64.encode(hash);

            return "hmac id=\"" + secretId + "\", algorithm=\"hmac-sha1\", headers=\"x-date x-source\", signature=\"" + sig + "\"";
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException | InvalidKeyException e) {
            log.error("计算鉴权签名异常", e);
            throw exception(AUTH_IDENTITY_ERROR);
        }
    }
    
    /**
     * 创建标准认证头信息
     */
    private Map<String, String> createAuthHeaders() {
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", "APPCODE " + appCode);
        headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        return headers;
    }
    
    /**
     * 执行HTTP POST请求
     */
    private HttpResult.Body executeHttpRequest(String url, Map<String, String> headers, Map<String, String> params) {
        try {
            return OkHttps.sync(url)
                    .addHeader(headers)
                    .addBodyPara(params)
                    .post()
                    .getBody()
                    .cache();
        } catch (Exception e) {
            log.error("HTTP请求执行异常: {}", url, e);
            throw new RuntimeException("API请求异常: " + e.getMessage());
        }
    }
    
    /**
     * 解析JSON结果并进行基础校验
     */
    private JSONObject parseResultJson(HttpResult.Body body, String operation) {
        if (body == null) {
            log.error("{}失败：API返回为空", operation);
            throw new RuntimeException(operation + "失败：API返回为空");
        }
        
        String response = body.toString();
        log.debug("{}返回结果: {}", operation, response);
        
        JSONObject jsonObject = JSON.parseObject(response);
        if (jsonObject == null) {
            log.error("{}失败：返回非JSON格式", operation);
            throw new RuntimeException(operation + "失败：返回非JSON格式");
        }
        
        return jsonObject;
    }
    
    /**
     * 处理人脸验证错误结果
     */
    private void handleFaceValidateError(int validateResult) {
        ErrorCode errorCode;
        
        switch (validateResult) {
            case -1:
                errorCode = new ErrorCode(100999, "身份证和姓名不一致");
                break;
            case -2:
                errorCode = new ErrorCode(100999, "公安库中无此身份证记录");
                break;
            case -3:
                errorCode = new ErrorCode(100999, "公安身份证库中没有此号码的照片");
                break;
            case -4:
                errorCode = new ErrorCode(100999, "照片参数不合格");
                break;
            case -5:
                errorCode = new ErrorCode(100999, "照片相片体积过大");
                break;
            case -6:
                errorCode = new ErrorCode(100999, "请检查图片编码");
                break;
            case -7:
                errorCode = new ErrorCode(100999, "照片相片体积过小");
                break;
            case 2:
                errorCode = new ErrorCode(100999, "系统分析可能为同一人");
                break;
            case 3:
                errorCode = new ErrorCode(100999, "系统分析为不是同人");
                break;
            case 4:
                errorCode = new ErrorCode(100999, "没检测到人脸");
                break;
            case 5:
                errorCode = new ErrorCode(100999, "疑似非活体");
                break;
            case 6:
                errorCode = new ErrorCode(100999, "出现多张脸");
                break;
            case 7:
                errorCode = new ErrorCode(100999, "身份证和姓名一致，官方人脸比对失败");
                break;
            default:
                errorCode = new ErrorCode(100999, "人脸验证失败，未知错误码：" + validateResult);
        }
        
        log.error("人脸验证失败: {}", errorCode.getMsg());
        throw exception(errorCode);
    }
    
    /**
     * 屏蔽敏感信息（如身份证号、银行卡号等）
     * 只显示后四位，其余用*代替
     */
    private String maskSensitiveInfo(String info) {
        if (StrUtil.isBlank(info) || info.length() <= 4) {
            return "****";
        }
        int length = info.length();
        return "*".repeat(length - 4) + info.substring(length - 4);
    }
    
    /**
     * 屏蔽敏感URL
     */
    private String maskSensitiveUrl(String url) {
        if (StrUtil.isBlank(url)) {
            return "****";
        }
        
        // 简单处理，只保留域名信息
        int protocolEnd = url.indexOf("://");
        if (protocolEnd < 0) {
            return "****";
        }
        
        int pathStart = url.indexOf("/", protocolEnd + 3);
        if (pathStart < 0) {
            return url;
        }
        
        return url.substring(0, pathStart) + "/****";
    }
}
